package org.example.core.util;

import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;

public class ReflectUtils {

    public static Field getDeclaredField(Class<?> clazz, String name) throws NoSuchFieldException {
        Class<?> cur = clazz;

        while (true) {
            try {
                return clazz.getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                cur = cur.getSuperclass();
                if (cur == Object.class) {
                    throw e;
                }
            }
        }
    }

    public static javassist.bytecode.annotation.Annotation toJavassistAnnotation(Annotation annotation, ConstPool cp) {
        try {
            javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(annotation.annotationType().getCanonicalName(), cp);
            for (Method method : annotation.annotationType().getMethods()) {
                if (method.getDeclaringClass() != annotation.annotationType()) {
                    continue;
                }
                Object value = method.invoke(annotation);
                Class<?> returnType = method.getReturnType();
                MemberValue memberValue = getMemberValue(cp, returnType, value);
                if (memberValue != null) {
                    ja.addMemberValue(method.getName(), memberValue);
                }
            }
            return ja;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static MemberValue getMemberValue(ConstPool cp, Class<?> type, Object value) throws InvocationTargetException, IllegalAccessException {
        if (type == float.class) {
            return new FloatMemberValue((Float) value, cp);
        } else if (type.isAnnotation()) {
            return new AnnotationMemberValue(toJavassistAnnotation((Annotation) value, cp), cp);
        } else if (type.isArray()) {
            Class<?> componentType = type.getComponentType();
            ArrayMemberValue arrayMemberValue = new ArrayMemberValue(cp);

            int length = Array.getLength(value);
            MemberValue[] memberValues = new MemberValue[length];
            for (int i = 0; i < length; i++) {
                Object o = Array.get(value, i);
                memberValues[i] = getMemberValue(cp, componentType, o);
            }
            arrayMemberValue.setValue(memberValues);

            return arrayMemberValue;
        } else if (type == short.class) {
            return new ShortMemberValue((Short) value, cp);
        } else if (type == double.class) {
            return new DoubleMemberValue((Double) value, cp);
        } else if (type == String.class) {
            return new StringMemberValue((String) value, cp);
        } else if (type == int.class) {
            return new IntegerMemberValue((Integer) value, cp);
        } else if (type == long.class) {
            return new LongMemberValue((Long) value, cp);
        } else if (type == Class.class) {
            return new ClassMemberValue(((Class<?>) value).getCanonicalName(), cp);
        } else if (type.isEnum()) {
            Enum<?> e = (Enum<?>) value;
            EnumMemberValue enumMemberValue = new EnumMemberValue(cp);
            enumMemberValue.setType(e.getDeclaringClass().getCanonicalName());
            enumMemberValue.setValue(e.name());
            return enumMemberValue;
        } else if (type == char.class) {
            return new CharMemberValue((Character) value, cp);
        } else if (type == byte.class) {
            return new ByteMemberValue((Byte) value, cp);
        } else if (type == boolean.class) {
            return new BooleanMemberValue((Boolean) value, cp);
        } else {
            return null;
        }
    }

}
